package com.app.androidtrackchanger;

import android.app.Activity;
import android.app.KeyguardManager;
import android.app.KeyguardManager.KeyguardLock;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {

	/* Buttons Enumeration */
	private enum ButtonsE
	{
		BUTTON_PREVIOUS,
		BUTTON_PLAY_PAUSE,
		BUTTON_NEXT,
		BUTTON_REPEAT
	};

	/* Play/Pause Button State Enumeration */
	private enum PlayPauseStateE
	{
		STATE_PLAYING,
		STATE_PAUSED
	};

	/* 'Blue' image fling enumeration */
	private enum FlingBlueIconPosE
	{
		POSITION_LEFT,
		POSITION_MIDDLE,
		POSITION_RIGHT
	}

	/* Global creation flag */
	private boolean mResourcesCreated = false;

	/* Previous Button */
	private Button    			 mPreviousButton 					 = null;
	private Drawable  			 mPreviousButtonDrawableImage 		 = null;
	private Drawable  		     mPreviousButtonPressedDrawableImage = null;
	private View.OnClickListener mPreviousButtonListener			 = null;

	/* Play/Pause Button */
	private Button    			 mPlayPauseButton					 = null;
	private Drawable  			 mPlayButtonDrawableImage			 = null;
	private Drawable  			 mPlayButtonPressedDrawableImage 	 = null;
	private Drawable  			 mPauseButtonDrawableImage			 = null;
	private Drawable  			 mPauseButtonPressedDrawableImage	 = null;
	private View.OnClickListener mPlayPauseButtonListener			 = null;

	/* Next Button */
	private Button    			 mNextButton 						 = null;
	private Drawable  			 mNextButtonDrawableImage			 = null;
	private Drawable  			 mNextButtonPressedDrawableImage	 = null;
	private View.OnClickListener mNextButtonListener				 = null;

	/* Repeat Button */
	private Button    			 mRepeatButton						 = null;
	private Drawable  			 mRepeatButtonActiveDrawableImage	 = null;
	private Drawable  			 mRepeatButtonInactiveDrawableImage	 = null;
	private View.OnClickListener mRepeatButtonListener				 = null;

	/* Current State of the music */
	private PlayPauseStateE mPlayPauseState;

	/* Audio Manager */
	private AudioManager mAudioManager;

	/* Image change delay */
	private int mSleepDelay 	= 100;
	private int mFlingIconDelay = 500;
	private int mPlaybackDelay  = 500;

	/* UI Refresh Resources */
	private Runnable 	   mRunnableThread       = null;
	private Handler  	   mHandler          	 = null;
	private CountDownTimer mPreviousButtonTimer  = null;
	private CountDownTimer mPlayButtonTimer      = null;
	private CountDownTimer mPauseButtonTimer     = null;
	private CountDownTimer mNextButtonTimer      = null;
	private CountDownTimer mPlaybackStartTimer   = null;

	/* Previous track fling resources */
	private ImageView mPreviousFlingPosLeft   = null;
	private ImageView mPreviousFlingPosMiddle = null;
	private ImageView mPreviousFlingPosRight  = null;

	private int mPreviousFlingGrayImage;
	private int mPreviousFlingBlueImage;

	/* Next track fling resources */
	private ImageView mNextFlingPosLeft   = null;
	private ImageView mNextFlingPosMiddle = null;
	private ImageView mNextFlingPosRight  = null;

	private int mNextFlingGrayImage;
	private int mNextFlingBlueImage;

	/* Fling 'Blue' icon position */
	private FlingBlueIconPosE mPreviousTrackFlingBlueIconPos;

	/* Gesture/fling detection resources */
	private static final int SWIPE_MIN_DISTANCE 	  = 35;
	private static final int SWIPE_MAX_OFF_PATH 	  = 250;
	private static final int SWIPE_THRESHOLD_VELOCITY = 200;

	private GestureDetector mSkipTrackGestureDetector = null;
	View.OnTouchListener    mSkipTrackGestureListener = null;

	/* Application Context */
	private Context mApplicationContext = null;

	/* Notification Manager */
	NotificationManager mNotificationManager = null;

	/* Notification Ids */
	private int mNotificationID;
	private final int ONGOING_NOTIFICATION_ID        = 0;
	private final int REGULAR_NOTIFICATION_IDS_START = 1;

	/* Keyguard Resources */
	private KeyguardManager mKeyguardManager = null;
	private KeyguardLock    mKeyguardlock    = null;
	private boolean         mScreenOn        = false;

	/* Broadcast Receiver */
	private BroadcastReceiver mBroadcastReceiver = null;

	/* Foreground activity resources */
	private Intent       mActivityIntent = null;
	private IntentFilter mIntentFilter   = null;

	/* Broadcast intents */
	private Intent mPreviousSongIntent = null;
	private Intent mNextSongIntent     = null;
	private Intent mTogglePauseIntent  = null;

	/* Metadata Resources */
	private TextView mMetadataArtist     = null;
	private TextView mMetadataAlbum      = null;
	private TextView mMetadataTrack      = null;
	private TextView mMetadataNowPlaying = null;

	/* Notification Resources */
	private Notification.Builder mBackButtonDisabledBuilder        = null;
	private Notification.Builder mPlaybackFailedBuilder            = null;
	private Notification.Builder mOngoingNotificationBuilder       = null;
	private Notification.Builder mRepeatSongOnNotificationBuilder  = null;
	private Notification.Builder mRepeatSongOffNotificationBuilder = null;

	/* Launch app from notification resources */
	private PendingIntent mPendingContentIntent = null;

	/* Logging resources */
	private final boolean PRINT_LOG_STMTS = false;

	/* Repeat flag */
	private boolean mRepeatSongActive = false;

	/* Track Change request flag, set when the user intends to change track */
	private boolean mTrackChangeRequested = false;
	
	/* Orientation Change flag */
	private boolean mOrientationChanged = false;
	
	/* Gesture detector (fling/swipe) class */
	class SkipTrackGestureDetector extends SimpleOnGestureListener {

		@Override
		public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {

			if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onFling START");

			try {
				if (Math.abs(event1.getY() - event2.getY()) > SWIPE_MAX_OFF_PATH)
					return false;

				if ( event1.getX() - event2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY ) { // Left Swipe

					// Broadcast the Previous song intent
					broadcastPreviousSongIntent();

					mTrackChangeRequested = true;

				}  else if ( event2.getX() - event1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY ) { // Right Swipe

					// Broadcast the Next song intent
					broadcastNextSongIntent();

					mTrackChangeRequested = true;

				}
			} catch (Exception e) {
				// Do nothing
			}

			if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onFling END");

			return false;

		} // End of function onFling

	} // End of class SkipTrackGestureDetector

	@Override
	protected void onCreate(Bundle savedInstanceState) {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onCreate START");

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		// If all resources are not already created
		if ( !mResourcesCreated ) {

			// Get all the resources needed
			createEverything();
			
			// Refresh handler and runnable thread
			createRefreshHandler();
			
			// Start the refresh cycle
			registerUIRefreshHandler();
			
			// Register stuff
			registerEverything();

		} else {

			if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onCreate :: Full-on creation skipped.");
		}

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onCreate END.");
	}
	
	/* Get all the resources needed, create objects and stuff */
	private void createEverything() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createEverything START.");
		
		// Storing Application Context for future usage
		mApplicationContext = getApplicationContext();

		// Notification Manager
		mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

		// Previous Track Button
		mPreviousButton 					 = (Button)findViewById(R.id.previous_button);
		mPreviousButtonDrawableImage 		 = getResources().getDrawable(R.drawable.ic_previous);
		mPreviousButtonPressedDrawableImage  = getResources().getDrawable(R.drawable.ic_previous_pressed);

		// Play Pause Button
		mPlayPauseButton 					 = (Button)findViewById(R.id.play_pause_button);
		mPlayButtonDrawableImage 		     = getResources().getDrawable(R.drawable.ic_play);
		mPlayButtonPressedDrawableImage 	 = getResources().getDrawable(R.drawable.ic_play_pressed);
		mPauseButtonDrawableImage 		     = getResources().getDrawable(R.drawable.ic_pause);
		mPauseButtonPressedDrawableImage	 = getResources().getDrawable(R.drawable.ic_pause_pressed);

		// Next Track Button
		mNextButton					   		 = (Button)findViewById(R.id.next_button);
		mNextButtonDrawableImage 			 = getResources().getDrawable(R.drawable.ic_next);
		mNextButtonPressedDrawableImage		 = getResources().getDrawable(R.drawable.ic_next_pressed);

		// Repeat Track Button
		mRepeatButton					   	 = (Button)findViewById(R.id.repeat_button);
		mRepeatButtonActiveDrawableImage 	 = getResources().getDrawable(R.drawable.ic_repeat_active);
		mRepeatButtonInactiveDrawableImage	 = getResources().getDrawable(R.drawable.ic_repeat_inactive);

		// Audio Manager
		mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

		// Previous track fling resources
		mPreviousFlingPosLeft   = (ImageView)findViewById(R.id.fling_previous_left);
		mPreviousFlingPosMiddle = (ImageView)findViewById(R.id.fling_previous_middle);
		mPreviousFlingPosRight  = (ImageView)findViewById(R.id.fling_previous_right);

		mPreviousFlingGrayImage = R.drawable.ic_fling_previous_1;
		mPreviousFlingBlueImage = R.drawable.ic_fling_previous_2;

		// Next track fling resources
		mNextFlingPosLeft       = (ImageView)findViewById(R.id.fling_next_left);
		mNextFlingPosMiddle     = (ImageView)findViewById(R.id.fling_next_middle);
		mNextFlingPosRight      = (ImageView)findViewById(R.id.fling_next_right);

		mNextFlingGrayImage		= R.drawable.ic_fling_foward_1;
		mNextFlingBlueImage		= R.drawable.ic_fling_foward_2;

		// Initializations
		mPreviousTrackFlingBlueIconPos = FlingBlueIconPosE.POSITION_MIDDLE;

		// Metadata TextView resources
		mMetadataArtist     = (TextView)findViewById(R.id.artist_textview);
		mMetadataAlbum      = (TextView)findViewById(R.id.album_textview);
		mMetadataTrack      = (TextView)findViewById(R.id.track_textview);
		mMetadataNowPlaying = (TextView)findViewById(R.id.now_playing_textview);

		// Create Button Click Listeners
		createButtonClickListeners();

		// Gesture detection
		mSkipTrackGestureDetector = new GestureDetector(this, new SkipTrackGestureDetector());
		mSkipTrackGestureListener = new View.OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				return mSkipTrackGestureDetector.onTouchEvent(event);
			}
		};

		// Global Notification ID
		mNotificationID = REGULAR_NOTIFICATION_IDS_START;

		// Keyguard resources
		mKeyguardManager = (KeyguardManager)getSystemService(Activity.KEYGUARD_SERVICE);
		mKeyguardlock    = mKeyguardManager.newKeyguardLock(KEYGUARD_SERVICE);
		mScreenOn        = true;

		// Create and register a Broadcast Receiver object
		createBroadcastReceiver();

		// Create countdown timers
		createCountDownTimers();

		// Create the song broadcast intents
		createSongBroadcastIntents();

		// Create notification builders
		createNotificationBuilders();

		// All resources have been created
		mResourcesCreated = true;
		
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createEverything END.");
	}
	
	/* Register the receivers and things like that */
	private void registerEverything() {

		// Set the button images and stuff
		displayUI();

		// Register Button Clicks
		registerButtonClickListeners();

		// Register the Broadcast Receiver
		registerBroadcastReceiver();

		// Registering for Swipe events
		registerForSwipeEvents();

	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		
		// Set the flag that the orientation just changed
		mOrientationChanged = true;

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onConfigurationChanged START.");

		// Checks the orientation of the screen
		if ( newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ) {

			// Set the "Landscape" view (tilted phone)
			setContentView(R.layout.activity_land_main);

		} else if ( newConfig.orientation == Configuration.ORIENTATION_PORTRAIT ) {

			// Set the "Potrait" view (default)
			setContentView(R.layout.activity_main);

		}
		
		// Get all the resources needed
		createEverything();

		// Register stuff
		registerEverything();

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onConfigurationChanged END.");
	}

	@Override
	protected void onResume() {
		super.onResume();

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onResume START.");

		// Re-create the UI
		displayUI();

		// Clear all the previously shown notifications
		if ( mNotificationManager != null ) {

			// Cancelling the notifications
			for ( int notificationId = REGULAR_NOTIFICATION_IDS_START; notificationId <= mNotificationID; ++notificationId ) {

				// Cancel the notification with the specified id
				mNotificationManager.cancel(notificationId);

			}

			mNotificationID = REGULAR_NOTIFICATION_IDS_START; // Reset the notification ID
		}

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "onResume END.");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		//getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	/* Create the button click listeners */
	private void createButtonClickListeners() {
		
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createButtonClickListeners START.");
		
		// Previous button click listener
		mPreviousButtonListener = new View.OnClickListener() {
			public void onClick(View v) {

				// Now toggle the UI again
				try {
					toggleUi(ButtonsE.BUTTON_PREVIOUS);
					broadcastPreviousSongIntent();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		
		// Play/Pause button click listener
		mPlayPauseButtonListener = new View.OnClickListener() {
			public void onClick(View v) {

				// Now toggle the UI again
				try {
					toggleUi(ButtonsE.BUTTON_PLAY_PAUSE);
					broadcastPlayOrPauseIntent();

					if ( mPlayPauseState == PlayPauseStateE.STATE_PAUSED ) {

						// Music just stopped playing
						mMetadataNowPlaying.setText(R.string.no_music_playing);
						mMetadataNowPlaying.setTextColor(Color.parseColor("#C0C0C0"));

					} else if ( mPlayPauseState == PlayPauseStateE.STATE_PLAYING ) {

						// Music just started playing
						mMetadataNowPlaying.setText(R.string.now_playing);
						mMetadataNowPlaying.setTextColor(Color.parseColor("#00FFFF"));

						// Incase playback fails, handle it
						mPlaybackStartTimer.start();
					}

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		
		// Next button click listener
		mNextButtonListener = new View.OnClickListener() {
			public void onClick(View v) {

				// Now toggle the UI again
				try {
					toggleUi(ButtonsE.BUTTON_NEXT);
					broadcastNextSongIntent();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		
		// Repeat button click listener
		mRepeatButtonListener = new View.OnClickListener() {
			public void onClick(View v) {

				// Now toggle the UI again
				try {
					toggleUi(ButtonsE.BUTTON_REPEAT);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
	
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createButtonClickListeners END.");
		
	} // End of function createButtonClickListeners()

	/* Registers the onClick listeners for all the UI buttons */
	private void registerButtonClickListeners() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "setButtonClickListener START.");

		// Previous Track Button
		mPreviousButton  .setOnClickListener(mPreviousButtonListener);

		// Play/Pause Button
		mPlayPauseButton .setOnClickListener(mPlayPauseButtonListener);

		// Next Track Button
		mNextButton      .setOnClickListener(mNextButtonListener);

		// Repeat Track Button
		mRepeatButton    .setOnClickListener(mRepeatButtonListener);

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "setButtonClickListener END.");
		
	} // End of function setButtonClickListener

	/* Toggles the image shown on the buttons */
	private void toggleUi(ButtonsE button) throws InterruptedException {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "toggleUi START.");

		switch ( button ) {

		case BUTTON_PREVIOUS:

			mPreviousButton      .setBackground(mPreviousButtonPressedDrawableImage);
			mPreviousButtonTimer .start();

			mTrackChangeRequested = true;

			break;
		case BUTTON_PLAY_PAUSE:

			switch ( mPlayPauseState ) {

			case STATE_PLAYING: // Currently playing music (STATE_PLAYING); new state will be STATE_PAUSED

				mPlayPauseState = PlayPauseStateE.STATE_PAUSED;

				mPlayPauseButton .setBackground(mPauseButtonPressedDrawableImage);
				mPlayButtonTimer .start();

				break;
			case STATE_PAUSED: // Currently NOT playing music (STATE_PAUSED); new state will be STATE_PLAYING

				mPlayPauseState = PlayPauseStateE.STATE_PLAYING;

				mPlayPauseButton  .setBackground(mPlayButtonPressedDrawableImage);
				mPauseButtonTimer .start();

				mTrackChangeRequested = true;

				break;
			}
			break;
		case BUTTON_NEXT:

			mNextButton      .setBackground(mNextButtonPressedDrawableImage);
			mNextButtonTimer .start();

			mTrackChangeRequested = true;

			break;

		case BUTTON_REPEAT:

			if ( mRepeatSongActive ) {

				// Repeat was active till now, the user doesn't want it anymore
				mRepeatButton.setBackground(mRepeatButtonInactiveDrawableImage);
				mRepeatSongActive = false;

				// Show the Repeat OFF notification to the user
				mNotificationManager.notify(mNotificationID++, mRepeatSongOffNotificationBuilder.build());

			} else {

				// The user wants the song to repeat
				mRepeatButton.setBackground(mRepeatButtonActiveDrawableImage);
				mRepeatSongActive = true;

				// Show the Repeat ON notification to the user
				mNotificationManager.notify(mNotificationID++, mRepeatSongOnNotificationBuilder.build());
			}

			break;

		} // End of switch ( button )

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "toggleUi END.");

	} // End of function toggleUi

	/* Sets the correct image backgrounds for the buttons */
	private void displayUI() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "displayUI START.");

		// Setting the default 'blue' button images
		mPreviousButton .setBackground(mPreviousButtonDrawableImage);
		mNextButton     .setBackground(mNextButtonDrawableImage);

		if ( mRepeatSongActive ) {

			// Set the "Repeat Active" icon
			mRepeatButton.setBackground(mRepeatButtonActiveDrawableImage);

		} else {

			// Set the "Repeat Inactive" icon
			mRepeatButton.setBackground(mRepeatButtonInactiveDrawableImage);
		}

		mPlayPauseState = mAudioManager.isMusicActive() ? PlayPauseStateE.STATE_PLAYING : PlayPauseStateE.STATE_PAUSED;

		switch ( mPlayPauseState ) {

		case STATE_PLAYING: // Currently playing music (STATE_PLAYING)
			mPlayPauseButton.setBackground(mPauseButtonDrawableImage);
			mMetadataNowPlaying.setText(R.string.now_playing);
			mMetadataNowPlaying.setTextColor(Color.parseColor("#00FFFF"));
			break;
		case STATE_PAUSED: // Currently NOT playing music (STATE_PAUSED)
			mPlayPauseButton.setBackground(mPlayButtonDrawableImage);
			mMetadataNowPlaying.setText(R.string.no_music_playing);
			mMetadataNowPlaying.setTextColor(Color.parseColor("#C0C0C0"));
			break;
		}

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "displayUI END.");

	} // End of function displayUI()

	/* Create song broadcast intents */
	private void createSongBroadcastIntents() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createSongBroadcastIntents START.");

		// Previous Song
		mPreviousSongIntent = new Intent();
		mPreviousSongIntent.setAction("com.sec.android.app.music.musicservicecommand.previous");

		// Next Song
		mNextSongIntent = new Intent();
		mNextSongIntent.setAction("com.sec.android.app.music.musicservicecommand.next");

		// Toggle Pause
		mTogglePauseIntent = new Intent();
		mTogglePauseIntent.setAction("com.sec.android.app.music.musicservicecommand.togglepause");

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createSongBroadcastIntents END.");

	} // End of function createSongBroadcastIntents()

	/* Change song to next one */
	private void broadcastNextSongIntent() {

		// Broadcast the intent
		sendBroadcast(mNextSongIntent);
		
	} // End of function broadcastNextSongIntent()

	/* Change song to previous one */
	private void broadcastPreviousSongIntent() {

		// Broadcast the intent
		sendBroadcast(mPreviousSongIntent);
		
	} // End of function broadcastPreviousSongIntent

	/* Play or Pause the song */
	private void broadcastPlayOrPauseIntent() {

		// Broadcast the intent
		sendBroadcast(mTogglePauseIntent);
		
	} // End of function broadcastPlayOrPauseIntent()

	/* Creating the UI refresh callback */
	private void registerUIRefreshHandler() {

		// Register the runnable thread with the handler
		mHandler.postDelayed ( mRunnableThread, mFlingIconDelay );

	} // End of function registerUIRefreshHandler()

	/* Refresh the UI */
	private void refreshUI() {

		/* We want the 'Blue' icon to behave in the following way:
		 * When the icon is in the left-most position for the "Previous Track" fling, it needs to be in the right most position for the 
		 * "Next Track" fling. */
		switch ( mPreviousTrackFlingBlueIconPos ) {

		case POSITION_LEFT:   // Put the 'Blue' icon in the Left position

			mPreviousFlingPosLeft   .setImageResource(mPreviousFlingBlueImage);
			mPreviousFlingPosMiddle .setImageResource(mPreviousFlingGrayImage);
			mPreviousFlingPosRight  .setImageResource(mPreviousFlingGrayImage);

			// Based on the icon's position on the "Previous Track" fling image, 
			// we decide the position for the "Next Track" fling
			mNextFlingPosLeft       .setImageResource(mNextFlingGrayImage);
			mNextFlingPosMiddle     .setImageResource(mNextFlingGrayImage);
			mNextFlingPosRight      .setImageResource(mNextFlingBlueImage);

			mPreviousTrackFlingBlueIconPos = FlingBlueIconPosE.POSITION_RIGHT;

			break;
			
		case POSITION_MIDDLE: // Put the 'Blue' icon in the Middle position

			mPreviousFlingPosLeft   .setImageResource(mPreviousFlingGrayImage);
			mPreviousFlingPosMiddle .setImageResource(mPreviousFlingBlueImage);
			mPreviousFlingPosRight  .setImageResource(mPreviousFlingGrayImage);

			// Based on the icon's position on the "Previous Track" fling image, 
			// we decide the position for the "Next Track" fling
			mNextFlingPosLeft  	    .setImageResource(mNextFlingGrayImage);
			mNextFlingPosMiddle     .setImageResource(mNextFlingBlueImage);
			mNextFlingPosRight      .setImageResource(mNextFlingGrayImage);

			mPreviousTrackFlingBlueIconPos = FlingBlueIconPosE.POSITION_LEFT;

			break;
			
		case POSITION_RIGHT:  // Put the 'Blue' icon in the Right position

			mPreviousFlingPosLeft   .setImageResource(mPreviousFlingGrayImage);
			mPreviousFlingPosMiddle .setImageResource(mPreviousFlingGrayImage);
			mPreviousFlingPosRight  .setImageResource(mPreviousFlingBlueImage);

			// Based on the icon's position on the "Previous Track" fling image, 
			// we decide the position for the "Next Track" fling
			mNextFlingPosLeft       .setImageResource(mNextFlingBlueImage);
			mNextFlingPosMiddle	    .setImageResource(mNextFlingGrayImage);
			mNextFlingPosRight      .setImageResource(mNextFlingGrayImage);

			mPreviousTrackFlingBlueIconPos = FlingBlueIconPosE.POSITION_MIDDLE;

			break;

		} // End of switch ( mPreviousTrackFlingBlueIconPos )

	} // End of function refreshUI()

	/* Create a Broadcast Receiver */
	private void createBroadcastReceiver() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createBroadcastReceiver START.");

		// Bring the application back to the foreground intent
		mActivityIntent = new Intent(mApplicationContext, MainActivity.class);

		// Adding the necessary flags
		mActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		mActivityIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); // Needed to bring the app to the foreground
		mActivityIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);     // Needed to launch Home screen when back is pressed

		mActivityIntent.setAction(Intent.ACTION_MAIN);
		mActivityIntent.addCategory(Intent.CATEGORY_LAUNCHER);

		// The Broadcast listener object
		mBroadcastReceiver = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				String action = intent.getAction();

				if (Intent.ACTION_SCREEN_ON.equals(action)) {

					// Screen is now ON
					mScreenOn = true;

					// Evaluate if the Keyguard lock needs to be enabled
					evaluateKeyguardLock();

					// Bring the application back to the foreground
					if ( mAudioManager.isMusicActive() ) {

						mApplicationContext.startActivity(mActivityIntent);

					}

				} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {

					// Screen is now OFF
					mScreenOn = false;

					// Re-enable the keyguard
					mKeyguardlock.reenableKeyguard();

				} else {

					// If orientation didn't just change, and "Repeat" is active, replay the song
					if ( !mOrientationChanged && mRepeatSongActive ) {

						// Broadcast the previous song intent
						broadcastPreviousSongIntent();

					} 

					if ( !mRepeatSongActive || mTrackChangeRequested || mOrientationChanged ) {

						// Extra the song info (the non alinged :s are necessary to be alinged on the device)
						String artist = intent.getStringExtra("artist");
						String track  = intent.getStringExtra("track");
						String album  = intent.getStringExtra("album");

						// Set the Track name, its Artist and album to the fields
						mMetadataArtist.setText(artist);
						mMetadataTrack .setText(track);
						mMetadataAlbum .setText(album);

						// Reset the flag
						mTrackChangeRequested = false;
					}
					
					// Reset the flag
					mOrientationChanged = false;
					
				}
			}
		};

		// Create a filter with the broadcast intents we are interested in
		mIntentFilter = new IntentFilter();

		// The actions we are interested in
		mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);         // Notification for Screen turning ON
		mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);		  // Notification for Screen turning OFF
		mIntentFilter.addAction("com.android.music.metachanged"); // Notification for the music track being changed

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createBroadcastReceiver END.");

	} // End of function createAndRegisterBroadcastReceiver()
	
	/* Create the handler thread that refreshes the UI */
	private void createRefreshHandler() {
		
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createRefreshHandler START.");
		
		mHandler        = new Handler();
		mRunnableThread = new Runnable() {
			@Override
			public void run() {

				// Refresh the user interface
				refreshUI();

				// Register again for next callback
				registerUIRefreshHandler();
			}
		};
		
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createRefreshHandler END.");
		
	} // End of function createRefreshHandler()
	
	// Register the Broadcaster Receiver
	private void registerBroadcastReceiver() {
		
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "registerBroadcastReceiver START.");
		
		// Register for broadcasts of interest.
		registerReceiver(mBroadcastReceiver, mIntentFilter, null, null);
		
		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "registerBroadcastReceiver END.");
		
	} // End of function registerBroadcastReceiver()

	@Override
	public void onClick(View arg0) {

		// Sample code for notifications
		/*Notification.Builder mBuilder = null;
		NotificationManager mNotificationManager =
				(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

		mBuilder =
				new Notification.Builder(getApplicationContext())
		.setSmallIcon(R.drawable.ic_fling_foward_1)
		.setContentTitle("Generic Notification")
		.setContentText("Nothing to see here!")
		.setTicker("On CLick Called");

		mNotificationManager.notify(6, mBuilder.build());
		 */
	}; // End of function onClick()

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {

		if (keyCode == KeyEvent.KEYCODE_BACK) {

			// event.startTracking(); // Uncomment this line to track for a "long press" of the key

			// Show to the user that the "Back Button is Disabled" for this application
			mNotificationManager.notify(mNotificationID++, mBackButtonDisabledBuilder.build());

			return true;

		} // End of case KEYCODE_BACK
		else if (keyCode == KeyEvent.KEYCODE_HOME) {

			// If the screen is ON, it doesn't matter if the music is active, we have to show the Keyguard
			if ( mScreenOn ) {

				// Re-enable the keyguard
				mKeyguardlock.reenableKeyguard();

			} else { // Screen is OFF

				// Do nothing for now
			}

			return true;
		} // End of case KEYCODE_HOME

		return super.onKeyDown(keyCode, event);

	} // End of function onKeyDown()

	/* Evaluates if the Keyguard lock needs to be enabled */
	private void evaluateKeyguardLock() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "evaluateKeyguardLock START.");

		if ( mAudioManager.isMusicActive() ) {

			// Disable the keyguard as the music is active
			mKeyguardlock.disableKeyguard();

		} else {

			// Re-enable the keyguard as no music is active
			mKeyguardlock.reenableKeyguard();

		}

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "evaluateKeyguardLock END.");

	} // End of function evaluateKeyguardLock()

	/* Creates the countdown timers for the delayed image to be shown on the Previous, Play/Pause and Next buttons */
	private void createCountDownTimers() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createCountDownTimers START.");

		// Previous Track button's timer
		mPreviousButtonTimer = new CountDownTimer(mSleepDelay, mSleepDelay) {

			@Override
			public void onTick(long milliseconds){}

			@Override
			public void onFinish() {
				// After 100 milliseconds display the next image
				mPreviousButton.setBackground(mPreviousButtonDrawableImage);
			}
		};

		// Next Track button's timer
		mNextButtonTimer = new CountDownTimer(mSleepDelay, mSleepDelay) {

			@Override
			public void onTick(long milliseconds){}

			@Override
			public void onFinish() {
				//After 100 milliseconds display the next image
				mNextButton.setBackground(mNextButtonDrawableImage);
			}

		};

		// Pause button's timer
		mPauseButtonTimer = new CountDownTimer(mSleepDelay, mSleepDelay) {

			@Override
			public void onTick(long milliseconds){}

			@Override
			public void onFinish() {
				// After 100 milliseconds display the next image
				mPlayPauseButton.setBackground(mPauseButtonDrawableImage);
			}
		};

		// Play button's timer
		mPlayButtonTimer = new CountDownTimer(mSleepDelay, mSleepDelay) {

			@Override
			public void onTick(long milliseconds){}

			@Override
			public void onFinish() {
				// After 100 milliseconds display the next image
				mPlayPauseButton.setBackground(mPlayButtonDrawableImage);
			}
		};

		// Timer to look for playback when the user pushes the 'Play' button
		mPlaybackStartTimer = new CountDownTimer(mPlaybackDelay, mPlaybackDelay) {

			@Override
			public void onTick(long milliseconds){}

			@Override
			public void onFinish() {

				if ( !mAudioManager.isMusicActive() ) {

					// Play button was pressed, but the playback didn't start
					// Now after 500 milliseconds, take evasive action

					// Show the blue 'Play' button
					mPlayPauseButton.setBackground(mPlayButtonDrawableImage);

					// Set the global play state to 'Paused'
					mPlayPauseState = PlayPauseStateE.STATE_PAUSED;

					// Couldn't start the music
					mMetadataNowPlaying.setText(R.string.no_music_playing);
					mMetadataNowPlaying.setTextColor(Color.parseColor("#C0C0C0"));

					// Show the playback failure notification
					mNotificationManager.notify(mNotificationID++, mPlaybackFailedBuilder.build());

					// Reset the track change requested flag
					mTrackChangeRequested = true;

				} else {

					// The music is playing
					mMetadataNowPlaying.setText(R.string.now_playing);
					mMetadataNowPlaying.setTextColor(Color.parseColor("#00FFFF"));
				}
			}
		};

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createCountDownTimers END.");

	} // End of function createCountDownTimers()

	/* Register for swipe/fling events */
	private void registerForSwipeEvents() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "registerForSwipeEvents START.");

		// Previous track leftmost icon
		mPreviousFlingPosLeft   .setOnClickListener(MainActivity.this);
		mPreviousFlingPosLeft   .setOnTouchListener(mSkipTrackGestureListener);

		// Previous track middle icon
		mPreviousFlingPosMiddle .setOnClickListener(MainActivity.this);
		mPreviousFlingPosMiddle .setOnTouchListener(mSkipTrackGestureListener);

		// Previous track rightmost icon
		mPreviousFlingPosRight  .setOnClickListener(MainActivity.this);
		mPreviousFlingPosRight  .setOnTouchListener(mSkipTrackGestureListener);

		// Next track leftmost icon
		mNextFlingPosLeft  		.setOnClickListener(MainActivity.this);
		mNextFlingPosLeft  	    .setOnTouchListener(mSkipTrackGestureListener);

		// Next track middle icon
		mNextFlingPosMiddle  	.setOnClickListener(MainActivity.this);
		mNextFlingPosMiddle     .setOnTouchListener(mSkipTrackGestureListener);

		// Next track rightmost icon
		mNextFlingPosRight  	.setOnClickListener(MainActivity.this);
		mNextFlingPosRight      .setOnTouchListener(mSkipTrackGestureListener);

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "registerForSwipeEvents END.");

	} // End of function registerForSwipeEvents()

	private void createNotificationBuilders() {

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createNotificationBuilders START.");

		// Creating a notification to show to the user 
		// that the "Back Button is Disabled" for this application
		mBackButtonDisabledBuilder = new Notification.Builder(mApplicationContext)
		.setSmallIcon(R.drawable.ic_nav_back)
		.setContentTitle("Skip Track")
		.setContentText("Back button disabled for this app")
		.setTicker("Back button is disabled. Press the 'Home' key.");

		// Creating a notification to show to the user 
		// that the play-back has failed
		mPlaybackFailedBuilder = new Notification.Builder(mApplicationContext)
		.setSmallIcon(R.drawable.ic_play_pressed)
		.setContentTitle("Skip Track")
		.setContentText("Playback failed!")
		.setTicker("Playback failed! Use MusicPlayer to start music.");

		// Create the "OnGoing" notification to be shown permanently to launch the app
		mPendingContentIntent = PendingIntent.getActivity(mApplicationContext, 0, mActivityIntent, 0);

		mOngoingNotificationBuilder = new Notification.Builder(mApplicationContext)
		.setSmallIcon(R.drawable.ic_launcher)
		.setOngoing(true)
		.setWhen(System.currentTimeMillis())
		.setContentTitle("Skip Track")
		.setContentText("Touch here to launch the app.")
		.setContentIntent(mPendingContentIntent)
		.setTicker("");

		// This "ongoing" notification takes the ID 0 which is reserved for this
		mNotificationManager.notify(ONGOING_NOTIFICATION_ID, mOngoingNotificationBuilder.build());

		// Creating a notification to show to the user 
		// that the repeat has been turned ON
		mRepeatSongOnNotificationBuilder = new Notification.Builder(mApplicationContext)
		.setSmallIcon(R.drawable.ic_repeat)
		.setContentTitle("Skip Track")
		.setContentText("Repeat Turned ON")
		.setTicker("Repeat Turned ON.");

		// Creating a notification to show to the user 
		// that the repeat has been turned ON
		mRepeatSongOffNotificationBuilder = new Notification.Builder(mApplicationContext)
		.setSmallIcon(R.drawable.ic_repeat)
		.setContentTitle("Skip Track")
		.setContentText("Repeat Turned OFF")
		.setTicker("Repeat Turned OFF.");

		if ( PRINT_LOG_STMTS ) Log.v("Skip Track", "createNotificationBuilders END.");
	}

} // End of class MainActivity
